home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / QT MovieToolBox / other QT code not used yet / ConvertToMovie Jr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-25  |  25.3 KB  |  775 lines  |  [TEXT/KAHL]

  1.  
  2. //    ConvertToMovie Jr. is an example application to recompress QuickTime
  3. //    movies and is based heavily on the venerable ConvertToMovieā„¢.  It shows
  4. //    how to use various parts of the Movie Toolbox, Image Compression Manager,
  5. //    and Standard Compression Component.
  6. //
  7. //    This code is not intended to show coding style, user interface
  8. //    or graceful error handling.
  9.  
  10.  
  11.  
  12. #include <traps.h>
  13. #include <movies.h>
  14. #include <moviesformat.h>
  15. #include <quicktimecomponents.h>
  16.  
  17.  
  18. #define    BailNil(n)        if (!n) goto bail;
  19. #define    BailError(n)    if (n) goto bail;
  20.  
  21. pascal short
  22. DefaultsHookProc(DialogPtr theDialog, short itemHit, void *params, long refcon);
  23.  
  24.  
  25. void main()
  26. {
  27.     short        frameNum;
  28.     OSErr        result;
  29.     short        abort;
  30.     short        firstPass = true;
  31.     
  32.     Point        where;
  33.     SFTypeList    typeList;
  34.     SFReply        srcFile;
  35.     SFReply        dstFile;
  36.     short        movieRefNum = 0;
  37.     
  38.     ComponentInstance ci;
  39.     
  40.     Movie        srcMovie = nil;
  41.     Rect        srcRect;
  42.     GWorldPtr    srcGWorld = nil;
  43.     Movie        dstMovie = nil;
  44.     Track        dstTrack;
  45.     Media        dstMedia;
  46.     long        frameCount;
  47.     TimeValue    curMovieTime;
  48.     
  49.     CWindowPtr    progressWindow = nil;
  50.     
  51.     ImageSequence    dstSeqID;
  52.     
  53.     ImageDescription **idh;
  54.     SCTemporalSettings ts;
  55.     SCDataRateSettings ds;
  56.     
  57.     InitGraf(&qd.thePort);
  58.     InitFonts();
  59.     FlushEvents(0xffff,0);
  60.     InitWindows();
  61.     InitMenus();
  62.     InitDialogs(0);
  63.     TEInit();
  64.     InitCursor();
  65.     MaxApplZone();
  66.     
  67.     
  68.     //---------------------------------------------------------------------------------------------
  69.     //    Check to make sure at least QuickTime 1.5 is installed.  We depend on features
  70.     //    of the Standard Compression component that are only present in 1.5 or later.
  71.     //---------------------------------------------------------------------------------------------
  72.     
  73.     {
  74.         long version;
  75.         
  76.         if (GetOSTrapAddress(_GestaltDispatch) == GetToolTrapAddress(_Unimplemented))
  77.             BailNil(0);
  78.         result = Gestalt(gestaltQuickTime,&version);
  79.         if (result || version < 0x01508000)
  80.             BailNil(0);
  81.     }
  82.     
  83.  
  84.     //---------------------------------------------------------------------------------------------
  85.     //    Initialize the Movie Toolbox.
  86.     //    Make sure you do this.  It's very easy to forget about.
  87.     //---------------------------------------------------------------------------------------------
  88.     
  89.     result = EnterMovies();
  90.     BailError(result);
  91.     
  92.  
  93.     //---------------------------------------------------------------------------------------------
  94.     //    Open the Standard Compression component and adjust it to meet our needs.
  95.     //---------------------------------------------------------------------------------------------
  96.     
  97.     ci = OpenDefaultComponent(StandardCompressionType,StandardCompressionSubType);
  98.     BailNil(ci);
  99.     
  100.     //    Turn off best depth option in the compression dialog.
  101.     //    Because all of our buffering is done at 32-bits, regardless
  102.     //    of the depth of the source data, best depth isn't very useful
  103.     //    as it will always choose 32-bit.
  104.     //
  105.     //    A more ambitious approach would be to loop through each of the
  106.     //    video sample descriptions in each of the video tracks looking
  107.     //    for the deepest depth, and using that for the best depth.
  108.     //    Better yet, we could find out which compressors were used
  109.     //    and set one of those as the default in the compression dialog.
  110.     
  111.     {
  112.         long flags;
  113.         
  114.         SCGetInfo(ci,scPreferenceFlagsType,&flags);
  115.         flags &= ~scShowBestDepth;
  116.         SCSetInfo(ci,scPreferenceFlagsType,&flags);
  117.     }
  118.     
  119.     //    Because we are recompressing a movie that may have a variable frame rate,
  120.     //    we want to allow the user to leave the frame rate text field blank.  We
  121.     //    can then preserve the frame durations of the source movie.  If they enter
  122.     //    a number we will resample the movie at a new frame rate.  If we don't clear
  123.     //    this flag, the compression dialog will not allow zero in the frame rate field.
  124.     //
  125.     //    NOTE: Setting this flag could have been done above when we cleared the
  126.     //    scShowBestDepth flag.  It is done separately for clarity.
  127.     
  128.     {
  129.         long flags;
  130.         
  131.         SCGetInfo(ci,scPreferenceFlagsType,&flags);
  132.         flags |= scAllowZeroFrameRate;
  133.         SCSetInfo(ci,scPreferenceFlagsType,&flags);
  134.     }
  135.     
  136.  
  137.     //---------------------------------------------------------------------------------------------
  138.     //    Ask for a source movie file using Standard Preview.
  139.     //    Use (-2,-2) to center on the best device.
  140.     //---------------------------------------------------------------------------------------------
  141.  
  142. NextPass:
  143.     where.h = where.v = -2;
  144.     typeList[0] = 'MooV';
  145.     SFGetFilePreview(where,"\p", nil,1,typeList,nil, &srcFile);
  146.     if (!srcFile.good)
  147.         goto bail;
  148.     
  149.     
  150.     //---------------------------------------------------------------------------------------------
  151.     //    Open up the movie file and get a movie from it.
  152.     //---------------------------------------------------------------------------------------------
  153.     
  154.     {
  155.         FSSpec    theFSSpec;
  156.         short    refnum;
  157.         
  158.         //    Create an FSSpec from the reply information.
  159.         
  160.         result = FSMakeFSSpec(srcFile.vRefNum,0,srcFile.fName,&theFSSpec);
  161.         BailError(result);
  162.         
  163.         //    Open a movie file using the FSSpec and create a movie from that file.
  164.         
  165.         result = OpenMovieFile(&theFSSpec, &refnum, 0);
  166.         BailError(result);
  167.         
  168.         result = NewMovieFromFile(&srcMovie,refnum, nil, nil,newMovieActive, nil);
  169.         BailError(result);
  170.         
  171.         //    We're done with the movie file.
  172.         
  173.         CloseMovieFile(refnum);
  174.     }
  175.     
  176.     
  177.     //---------------------------------------------------------------------------------------------
  178.     //    Count the number of video "frames" in the movie by stepping through all of the
  179.     //    video "interesting times", or in other words, the places where the movie displays
  180.     //    a new video sample. The time between these interesting times is not necessarily constant.
  181.     //---------------------------------------------------------------------------------------------
  182.     
  183.     {
  184.         OSType        whichMediaType = VIDEO_TYPE;
  185.         short        flags = nextTimeMediaSample + nextTimeEdgeOK;
  186.         TimeValue    duration;
  187.         TimeValue    theTime = 0;
  188.         
  189.         frameCount = -1;
  190.         while (theTime >= 0) {
  191.             frameCount++;
  192.             GetMovieNextInterestingTime(srcMovie,flags,1,&whichMediaType,theTime,0,&theTime,&duration);
  193.             
  194.             //    After the first interesting time, don't include the time we are currently at.
  195.             
  196.             flags = nextTimeMediaSample;
  197.         }
  198.     }
  199.     
  200.     
  201.     //---------------------------------------------------------------------------------------------
  202.     //    Get the bounding rectangle of the movie, create a 32-bit gworld with those dimensions,
  203.     //    and draw the movie poster picture into it.  This gworld will be used for the test image
  204.     //    in the compression dialog and for rendering movie frames into.
  205.     //---------------------------------------------------------------------------------------------
  206.     
  207.     {
  208.         PicHandle    ph = GetMoviePosterPict(srcMovie);
  209.         CGrafPtr    savePort;
  210.         GDHandle    saveDevice;
  211.         
  212.         result = GetMoviesError();
  213.         GetMovieBox(srcMovie,&srcRect);
  214.         result = NewGWorld(&srcGWorld,32,&srcRect,nil,nil,0);
  215.         BailError(result);
  216.         if (ph) {
  217.             GetGWorld(&savePort,&saveDevice);
  218.             SetGWorld(srcGWorld,nil);
  219.             EraseRect(&srcRect);
  220.             DrawPicture(ph,&srcRect);
  221.             KillPicture(ph);
  222.             SetGWorld(savePort,saveDevice);
  223.  
  224.             //    Use the gworld image of the movie poster frame as a compression dialog test image.
  225.             //    Pass nil for srcRect to use the entire image, and pass zero to use the default display method.
  226.         
  227.             result = SCSetTestImagePixMap(ci,srcGWorld->portPixMap,nil,0);
  228.             BailError(result);
  229.         }
  230.     }
  231.     
  232.     //---------------------------------------------------------------------------------------------
  233.     //    Set compression dialog extend procs for the "Defaults" button.
  234.     //    We wait until now so we can pass the srcGWorld pixmap in as a refcon.
  235.     //---------------------------------------------------------------------------------------------
  236.  
  237.     {
  238.         SCExtendedProcs xprocs;
  239.         
  240.         //    Because the compression dialog is movable modal, a filter proc
  241.         //    to handle update events for application windows is required.
  242.         //    Since our example application is completely modal, and we have
  243.         //    no other windows to update, we don't have a filter proc.
  244.         
  245.         xprocs.filterProc = nil;
  246.         
  247.         //    Proc to handle custom button click.
  248.         
  249.         xprocs.hookProc = DefaultsHookProc;
  250.         
  251.         //    Any information useful to the extended procs can be put
  252.         //    in the refcon.  For an application that has globals, a refcon
  253.         //    of the current A5 is handy for getting at those globals in
  254.         //    the extended procs.  In our case, we put the srcGWorld pixmap
  255.         //    in the refcon so we can set defaults on it in our hook proc.
  256.         
  257.         xprocs.refcon = (long)srcGWorld->portPixMap;
  258.         
  259.         //    Set the custom button name.
  260.         
  261.         BlockMove("\pDefaults",xprocs.customName,9);
  262.         
  263.         //    Tell the compression dialog about the extended procs.
  264.         
  265.         SCSetInfo(ci,scExtendedProcsType,&xprocs);
  266.     }
  267.     
  268.     
  269.     //---------------------------------------------------------------------------------------------
  270.     //    Set up some default settings for the compression dialog if needed, and ask
  271.     //    for compression settings from the user.
  272.     //---------------------------------------------------------------------------------------------
  273.     
  274.     if (firstPass) {
  275.         result = SCDefaultPixMapSettings(ci,srcGWorld->portPixMap,true);
  276.         BailError(result);
  277.     }
  278.     
  279.     result = SCGetInfo(ci,scTemporalSettingsType,&ts);
  280.     BailError(result);
  281.     
  282.     //    The first time through, clear out the default frame rate chosen by Standard Compression.
  283.     //    We know that a frame rate of 0 means use the rate of the source movie.  It probably should
  284.     //    have been smart enough to figure that out because we set the scAllowZeroFrameRate flag.
  285.     //    Oh well.
  286.     
  287.     if (firstPass) {
  288.         ts.frameRate = 0;
  289.         SCSetInfo(ci,scTemporalSettingsType,&ts);
  290.     }
  291.     
  292.     //    Get compression settings from the user.  The first time through this loop,
  293.     //    we choose default compression settings for the test image we set above.
  294.     //    On subsequent passes, the settings previously chosen by the user will be the defaults.
  295.     
  296.     result = SCRequestSequenceSettings(ci);
  297.     if (result == scUserCancelled) {
  298.         // deal with user cancelling.
  299.     }
  300.     BailError(result);
  301.     
  302.     //    Get a copy of the temporal settings the user entered.  We'll need them for
  303.     //    some of our calculations.  In a simpler application we'd never have to look at them.
  304.     
  305.     result = SCGetInfo(ci,scTemporalSettingsType,&ts);
  306.     BailError(result);
  307.     
  308.     
  309.     //---------------------------------------------------------------------------------------------
  310.     //    Take the overall data rate value entered by the user and subtract out the
  311.     //    part of the data rate allocated to sound.  We do this by looking at all of the
  312.     //    sound tracks in the source movie and using the one with the highest sample rate.
  313.     //    This number is then subtracted from the data rate leaving the amount of data per
  314.     //    second available to the compressed video data.  This is obviously only an approximation
  315.     //    because the various sound tracks may or may not overlap, be stereo, be compressed, etc.
  316.     //    A more accurate approximation would be almost mandatory for a real QuickTime movie
  317.     //    creating/editing application.
  318.     //---------------------------------------------------------------------------------------------
  319.     
  320.     if (!SCGetInfo(ci,scDataRateSettingsType,&ds)) {
  321.         if (ds.dataRate) {
  322.             short    i;
  323.             short    trackCount = GetMovieTrackCount(srcMovie);
  324.             long    maxSoundRate = 0;
  325.             for (i = 1; i <= trackCount; i++) {
  326.                 OSType    trackType;
  327.                 Track    strack = GetMovieIndTrack(srcMovie,i);
  328.                 Media    smedia = GetTrackMedia(strack);
  329.                 BailError(GetMoviesError());
  330.                 GetMediaHandlerDescription(smedia,&trackType,0,0);
  331.                 if (trackType == SOUND_TYPE) {
  332.                     long rate;
  333.                     SampleDescriptionHandle desc = (SampleDescriptionHandle)NewHandle(sizeof(SampleDescription));
  334.                     GetMediaSampleDescription(smedia,1,desc);
  335.                     if (GetMoviesError()) {
  336.                         DisposeHandle((Handle)desc);
  337.                         continue;
  338.                     }
  339.                     rate = (*(SoundDescriptionHandle)desc)->sampleRate >> 16;
  340.                     if (rate > maxSoundRate)
  341.                         maxSoundRate = rate;
  342.                 }
  343.             }
  344.             ds.dataRate -= maxSoundRate;
  345.         }
  346.         SCSetInfo(ci,scDataRateSettingsType,&ds);
  347.     }
  348.  
  349.  
  350.     //---------------------------------------------------------------------------------------------
  351.     //    If the user said they want to resample the frame rate of the movie,
  352.     //    (by entering a non-zero value in the frame rate field) calculate
  353.     //    the number of frames and duration for the new movie.
  354.     //---------------------------------------------------------------------------------------------
  355.     
  356.     if (ts.frameRate) {
  357.         long    dur = GetMovieDuration(srcMovie);
  358.         long    timescale = GetMovieTimeScale(srcMovie);
  359.         float    f = (float)dur * ts.frameRate;
  360.         frameCount = f / timescale / 65536;
  361.         if (frameCount == 0)
  362.             frameCount = 1;
  363.     }
  364.     
  365.     
  366.     //---------------------------------------------------------------------------------------------
  367.     //    Ask the user for the name of the new movie file.
  368.     //    We'll be lazy here and just use "Untitled".  A real app would
  369.     //    base it on the name of the source movie and check if the user
  370.     //    tried to enter a name the same as the source movie.
  371.     //
  372.     //    Note we use the SCPositionDialog call to get a good position
  373.     //    point for the SFPutFile.  -3999 is the resource I.D. for
  374.     //    SFPutFile dialog.
  375.     //---------------------------------------------------------------------------------------------
  376.     
  377.     where.h = where.v = -2;
  378.     SCPositionDialog(ci,-3999,&where);
  379.     SFPutFile(where,"\pSave new movie file as:","\pUntitled",nil,&dstFile);
  380.     if (!dstFile.good)
  381.         goto bail;            // deal with user cancelling
  382.     
  383.     
  384.     //---------------------------------------------------------------------------------------------
  385.     //    Open a progress window to frames as they are compressed.  Use Standard
  386.     //    Compression utility routines to position the window.
  387.     //---------------------------------------------------------------------------------------------
  388.     
  389.     {
  390.         Rect r = srcRect;
  391.         where.h = where.v = -2;
  392.         result = SCPositionRect(ci,&r,&where);
  393.         progressWindow = (CWindowPtr)NewCWindow(0,&r,dstFile.fName,true,0,(WindowPtr)-1,false,0);
  394.     }
  395.     
  396.     
  397.     //---------------------------------------------------------------------------------------------
  398.     //    Create the new movie file and prepare it for edits.
  399.     //---------------------------------------------------------------------------------------------
  400.     
  401.     {
  402.         FSSpec            theFSSpec;
  403.         MatrixRecord    matrix;
  404.         
  405.         //    Create an FSSpec for the destination movie.
  406.         
  407.         result = FSMakeFSSpec(dstFile.vRefNum,0,dstFile.fName,&theFSSpec);
  408.         if (result != fnfErr)        // ignore a file not found error
  409.             BailError(result);
  410.         
  411.         //    Using the FSSpec create a movie file for the destination movie.
  412.         
  413.         result = CreateMovieFile(&theFSSpec,'TVOD',0,createMovieFileDeleteCurFile,&movieRefNum,&dstMovie);
  414.         BailError(result);
  415.         
  416.         //    Create a new video movie track with the same dimensions as the entire source movie.
  417.         
  418.         dstTrack = NewMovieTrack(dstMovie,
  419.                 (long)(srcRect.right - srcRect.left) << 16,
  420.                 (long)(srcRect.bottom - srcRect.top) << 16,0);
  421.         
  422.         //    Create a media for the new track with the same time scale as the
  423.         //    source movie.  Because the time scales are the same, we don't have
  424.         //    to do any time scale conversions.
  425.         
  426.         dstMedia = NewTrackMedia(dstTrack,VIDEO_TYPE,GetMovieTimeScale(srcMovie),0,0);
  427.         BailError(GetMoviesError());
  428.         
  429.         //    Copy the user data and settings from the source to the dest movie.
  430.         //    These settings include information like user data.
  431.         
  432.         CopyMovieSettings(srcMovie,dstMovie);
  433.         
  434.         //    Set movie matrix to identity and clear the movie clip region
  435.         //    because conversion process transforms and composites all video
  436.         //    tracks into one untransformed video track.
  437.         
  438.         SetIdentityMatrix(&matrix);
  439.         SetMovieMatrix(dstMovie,&matrix);
  440.         SetMovieClipRgn(dstMovie,nil);
  441.         
  442.         //    Prepare for adding frames to the movie.
  443.         //    Make sure you do this.  It's very easy to forget about.
  444.         
  445.         result = BeginMediaEdits(dstMedia);
  446.         BailError(result);
  447.     }
  448.     
  449.  
  450.     //---------------------------------------------------------------------------------------------
  451.     //    Start a compression sequence using the parameters chosen by the user.
  452.     //    Pass nil for the source rect to use the entire image.  An image description
  453.     //    handle will be returned in idh.  Nil could be passed for that if we
  454.     //    didn't need it for our progress window display.  We do not have dispose
  455.     //    the image description handle.  It is disposed for use by SCCompressSequenceEnd.
  456.     //---------------------------------------------------------------------------------------------
  457.     
  458.     result = SCCompressSequenceBegin(ci,srcGWorld->portPixMap,nil,&idh);
  459.     BailError(result);
  460.     
  461.     //    Clear out our image gworld and set movie to draw into it.
  462.  
  463.     SetGWorld(srcGWorld,nil);
  464.     EraseRect(&srcGWorld->portRect);
  465.     SetMovieGWorld(srcMovie,srcGWorld,GetGWorldDevice(srcGWorld));
  466.     
  467.     //    Set current time value to begining of movie.
  468.     
  469.     curMovieTime = 0;
  470.     
  471.     //    Loop through all of the interesting times we counted above.
  472.     
  473.     for (frameNum = 0; frameNum < frameCount; frameNum++) {
  474.         
  475.         short            syncFlag;
  476.         TimeValue        duration;
  477.         long            flags;
  478.         long            dataSize;
  479.         Handle            compressedData;
  480.         
  481.         //    Abort if the user clicked the mouse or pressed a key.
  482.         
  483.         {
  484.             EventRecord event;
  485.             abort = false;
  486.             if (EventAvail(keyDownMask | mDownMask,&event)) {
  487.                 abort = true;
  488.                 break;
  489.             }
  490.         }
  491.         
  492.         //    Get the next frame of the source movie.
  493.         
  494.         {
  495.             //    If we are resampling the movie, step to the next frame.
  496.             
  497.             if (ts.frameRate) {
  498.                 
  499.                 //    This code could be much smarter about its calculations.
  500.                 //    The srcMovie duration and dstMovie frame duration are both
  501.                 //    constant and could be calculated once outside this loop
  502.                 
  503.                 long dur = GetMovieDuration(srcMovie);
  504.                 curMovieTime = frameNum*dur/(frameCount-1);
  505.                 duration = dur / frameCount;
  506.             } else {
  507.                 short flags = nextTimeMediaSample;
  508.                 OSType    whichMediaType = VIDEO_TYPE;
  509.                 
  510.                 //    If this is the first frame, include the frame we are currently on.
  511.                 
  512.                 if (frameNum == 0)
  513.                     flags |= nextTimeEdgeOK;
  514.                 
  515.                 //    If we are maintaining the frame durations of the source movie,
  516.                 //    skip to the next interesting time and get the duration for that frame.
  517.                 
  518.                 GetMovieNextInterestingTime(srcMovie,flags,1,&whichMediaType,curMovieTime,0,&curMovieTime,&duration);
  519.             }
  520.             SetMovieTimeValue(srcMovie,curMovieTime);
  521.             MoviesTask(srcMovie,0);
  522.             MoviesTask(srcMovie,0);
  523.             MoviesTask(srcMovie,0);
  524.         }
  525.         
  526.         //    If data rate constraining is being done, tell Standard Compression the
  527.         //    duration of the current frame in milliseconds.  We only need to do this
  528.         //    if the frames have variable durations.
  529.         
  530.         {
  531.             SCDataRateSettings ds;
  532.             if (!SCGetInfo(ci,scDataRateSettingsType,&ds)) {
  533.                 ds.frameDuration = duration * 1000 / GetMovieTimeScale(srcMovie);
  534.                 SCSetInfo(ci,scDataRateSettingsType,&ds);
  535.             }
  536.         }
  537.         
  538.         //    Compress the frame.  compressedData will hold a handle to the newly compressed
  539.         //    image data.  dataSize is the size of the compressed data, which will usually be
  540.         //    different than the size of the compressedData handle.  syncFlag is a value that
  541.         //    can be passed directly to AddMediaSample which indicates whether or not the frame
  542.         //    is a key frame.  Note that we do not have to dispose of the compressedData handle.
  543.         //    It will be dispose for us when we call SCCompressSequenceEnd.
  544.         
  545.         result = SCCompressSequenceFrame(ci,srcGWorld->portPixMap,&srcRect,&compressedData,&dataSize,&syncFlag);
  546.         BailError(result);
  547.         
  548.         //    Append the compressed image data to the media.
  549.         
  550.         result = AddMediaSample(dstMedia,compressedData,0,dataSize,duration,
  551.                 (SampleDescriptionHandle)idh,1,syncFlag,nil);
  552.         BailError(result);
  553.         
  554.         //    Decompress the compressed frame into the progress window.
  555.         
  556.         if (progressWindow) {
  557.             CodecFlags    outFlags;
  558.             char hstate;
  559.             
  560.             //    Set port to progress window.
  561.             
  562.             SetGWorld(progressWindow,nil);
  563.             
  564.             //    If this is the first frame, start up a decompression sequence.
  565.             
  566.             if (frameNum == 0) {
  567.                 result = DecompressSequenceBegin(&dstSeqID,idh,nil,nil,&srcRect,nil,ditherCopy,
  568.                     nil,0,codecNormalQuality,anyCodec);
  569.                 BailError(result);
  570.             }
  571.             
  572.             //    Save the locked state of the compressed data and then lock it.
  573.             //    We want it locked but standard compression may or may not.
  574.             
  575.             hstate = HGetState(compressedData);
  576.             HLock(compressedData);
  577.             
  578.             //    Decompress the frame to the progress window.  Note that we StripAddress
  579.             //    the compressedData pointer because it must be 32-bit clean.
  580.             
  581.             result = DecompressSequenceFrame(dstSeqID,StripAddress(*compressedData),0,nil,nil);
  582.             
  583.             //    Restore the locked state of the data handle.
  584.             
  585.             HSetState(compressedData,hstate);
  586.             BailError(result);
  587.         }
  588.     }
  589.     
  590.     //    Close the compression sequence.  This will dispose of the image description
  591.     //    and compressed data handles allocated by SCCompressSequenceBegin.
  592.     
  593.     SCCompressSequenceEnd(ci);
  594.     
  595.     //    Close the decompression sequence.  Note that this is an Image Compression
  596.     //    Manager call, not Standard Compression.
  597.     
  598.     CDSequenceEnd(dstSeqID);
  599.     
  600.     
  601.     //    Copy all sound tracks from the source to dest movie.
  602.     //
  603.     //    NOTE: We are not copying any other track types here.  That means
  604.     //    text tracks, alternate tracks, etc. are not being copied.  A real
  605.     //    application would give the user some options here.
  606.     
  607.     {
  608.         short    i;
  609.         short    trackCount;
  610.         
  611.         //    Get a count of all the tracks present in the source movie.
  612.         
  613.         trackCount = GetMovieTrackCount(srcMovie);
  614.         
  615.         //    Loop through each of the tracks, looking for sound tracks.
  616.         
  617.         for (i = 1; i <= trackCount; i++) {
  618.             OSType    trackType;
  619.             Track    strack;
  620.             Media    smedia;
  621.             
  622.             //    Get the next track and its media.
  623.             
  624.             strack = GetMovieIndTrack(srcMovie,i);
  625.             smedia = GetTrackMedia(strack);
  626.             BailError(GetMoviesError());
  627.             
  628.             //    Find out what type of media this track has.
  629.             //    We only care about sound.
  630.             
  631.             GetMediaHandlerDescription(smedia,&trackType,0,0);
  632.             if (trackType == SOUND_TYPE) {
  633.                 Track    dtrack;
  634.                 Media    dmedia;
  635.                 
  636.                 //    Create a new sound track in the destination movie.
  637.                 
  638.                 dtrack = NewMovieTrack(dstMovie,0,0,GetTrackVolume(strack));
  639.                 BailError(GetMoviesError());
  640.                 
  641.                 //    Create a media for that sound track and prepare it for editing.
  642.                 
  643.                 dmedia = NewTrackMedia(dtrack,SOUND_TYPE,GetMediaTimeScale(smedia),0,0);
  644.                 BailError(GetMoviesError());
  645.                 
  646.                 result = BeginMediaEdits(dmedia);
  647.                 BailError(GetMoviesError());
  648.                 
  649.                 //    Insert the new track into the dest movie starting at time
  650.                 //    zero and lasting for the entire duration of the movie.
  651.                 
  652.                 InsertTrackSegment(strack,dtrack,0,GetTrackDuration(strack),0);
  653.                 BailError(GetMoviesError());
  654.                 
  655.                 //    We're done editing the media.
  656.                 
  657.                 EndMediaEdits(dmedia);
  658.             }
  659.         }
  660.     }
  661.     
  662.     //---------------------------------------------------------------------------------------------
  663.     //    Now that we're finished compressing video data, make that data part of our movie. 
  664.     //---------------------------------------------------------------------------------------------
  665.     
  666.     if (dstTrack) {
  667.         short resID = 128;
  668.         
  669.         //    End changes to the media.
  670.         
  671.         result = EndMediaEdits(dstMedia);
  672.         BailError(result);
  673.         
  674.         //    Insert the newly created media into the newly created track at
  675.         //    the begining of the track and lasting for the entire duration of
  676.         //    the media.  The media rate is 1.0 for normal playback rate.
  677.         
  678.         InsertMediaIntoTrack(dstTrack,0,0,GetMediaDuration(dstMedia),kFix1);
  679.         
  680.         //    Add the movie resource to the dst movie file.
  681.         
  682.         result = AddMovieResource(dstMovie,movieRefNum,&resID,"\pMovie 1");
  683.         BailError(result);
  684.  
  685.         //---------------------------------------------------------------------------------------------
  686.         //    Next we flatten the movie.  The movie file we just created has all of
  687.         //    the video data at the beginning and all of the sound data at the end.
  688.         //    This unnatural situation may cause the movie to play poorly.  By flattening
  689.         //    the movie, we are reinterleaving all of the tracks in a way optimal for playback.
  690.         //    Because we cannot flatten a movie in place, we create a temp file, flatten to
  691.         //    that file and if successful, delete the first file and rename the temp file to
  692.         //    be the real file.  For large files this takes some time.
  693.         //
  694.         //    Once again, a real application would choose a temp file name that doesn't
  695.         //    possibly conflict with other file names, as well as delete the temp file
  696.         //    if the flatten fails.
  697.         //---------------------------------------------------------------------------------------------
  698.         
  699.         {
  700.             FSSpec    theFSSpec;
  701.             short    resID = 128;
  702.             dstFile.fName[++dstFile.fName[0]] = '@';
  703.             result = FSMakeFSSpec(dstFile.vRefNum,0,dstFile.fName,&theFSSpec);
  704.             if (result != fnfErr)
  705.                 BailError(result);
  706.             FlattenMovie(dstMovie,0,&theFSSpec,'TVOD',-1,createMovieFileDeleteCurFile,&resID,"\p");
  707.             result = GetMoviesError();
  708.             CloseMovieFile(movieRefNum);
  709.             DisposeMovie(dstMovie);
  710.             dstTrack = nil;
  711.             if (!result) {
  712.                 Str255 tempStr;
  713.                 BlockMove(dstFile.fName,tempStr,256);
  714.                 tempStr[0]--;
  715.                 result = FSDelete(tempStr,dstFile.vRefNum);
  716.                 result = Rename(dstFile.fName,dstFile.vRefNum,tempStr);
  717.             }
  718.         }
  719.     }
  720.     
  721.     //    Throw away the progress window.
  722.     
  723.     if (progressWindow) {
  724.         CloseWindow(progressWindow);
  725.         progressWindow = nil;
  726.     }
  727.     
  728.     //    Throw away the movie buffer gworld.
  729.     
  730.     if (srcGWorld) {
  731.         DisposeGWorld(srcGWorld);
  732.         srcGWorld = nil;
  733.     
  734.         //    Just to be overly safe, we clear our the test image
  735.         //    because we just disposed the pixmap it depends on.
  736.     
  737.         SCSetTestImagePixMap(ci,nil,nil,0);
  738.     }
  739.     
  740.     if (!abort) {
  741.         firstPass = false;
  742.         goto NextPass;
  743.     }
  744.     
  745.     //    Close the Standard Compression component.
  746.     
  747.     CloseComponent(ci);
  748. bail:;
  749. }
  750.  
  751.  
  752. //    Handle clicks in the "Defaults" custom button.
  753.  
  754. pascal short
  755. DefaultsHookProc(DialogPtr theDialog, short itemHit, void *params, long refcon)
  756. {
  757.     if (itemHit == scCustomItem) {
  758.         SCTemporalSettings ts;
  759.         
  760.         //    Set defaults for our test image (passed in refcon).
  761.         
  762.         SCDefaultPixMapSettings(params,(PixMapHandle)refcon,true);
  763.         
  764.         //    Once again, don't use the default frame rate chosen
  765.         //    by Standard Compression.  Clear it out to zero.
  766.         
  767.         SCGetInfo(params,scTemporalSettingsType,&ts);
  768.         ts.frameRate = 0;
  769.         SCSetInfo(params,scTemporalSettingsType,&ts);
  770.     }
  771.     return (itemHit);
  772. }
  773.  
  774.  
  775.